package com.example.ethereum.service;

import com.example.ethereum.dto.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.tx.gas.DefaultGasProvider;
import org.web3j.utils.Numeric;

import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
public class ContractService {

    @Autowired
    private Web3j web3j;

    @Autowired
    private Credentials credentials;

    @Autowired
    private DefaultGasProvider gasProvider;

    @Autowired
    private ContractCompilerService contractCompilerService;

    /**
     * 部署智能合约
     */
    public String deployContract(ContractDeployRequest request) throws Exception {
        log.info("Deploying contract with name: {}, symbol: {}, decimals: {}, initialSupply: {}",
                request.getName(), request.getSymbol(), request.getDecimals(), request.getInitialSupply());

        // 编译并获取合约字节码
        String contractBytecode = getContractBytecode();
        
        // 构建构造函数参数
        List<Type> constructorParams = Arrays.<Type>asList(
                new Utf8String(request.getName()),
                new Utf8String(request.getSymbol()),
                new Uint8(request.getDecimals()),
                new Uint256(BigInteger.valueOf(request.getInitialSupply())
                        .multiply(BigInteger.TEN.pow(request.getDecimals())))
        );

        // 编码构造函数参数
        String constructorData = FunctionEncoder.encodeConstructor(constructorParams);

        // 获取nonce
        EthGetTransactionCount ethGetTransactionCount = web3j
                .ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST)
                .send();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();

        // 创建原始交易
        RawTransaction rawTransaction = RawTransaction.createContractTransaction(
                nonce,
                gasProvider.getGasPrice(),
                gasProvider.getGasLimit(),
                BigInteger.ZERO,
                contractBytecode + constructorData
        );

        // 签名交易
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);

        // 发送交易
        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
        
        if (ethSendTransaction.hasError()) {
            throw new RuntimeException("Transaction failed: " + ethSendTransaction.getError().getMessage());
        }

        String transactionHash = ethSendTransaction.getTransactionHash();
        log.info("Contract deployment transaction hash: {}", transactionHash);

        // 等待交易确认
        TransactionReceipt receipt = waitForTransactionReceipt(transactionHash);
        String contractAddress = receipt.getContractAddress();
        
        log.info("Contract deployed at address: {}", contractAddress);
        return contractAddress;
    }

    /**
     * 铸币
     */
    public String mint(MintRequest request) throws Exception {
        log.info("Minting {} tokens to {}", request.getAmount(), request.getToAddress());

        // 构建mint函数调用
        Function function = new Function(
                "mint",
                Arrays.asList(
                        new org.web3j.abi.datatypes.Address(request.getToAddress()),
                        new Uint256(BigInteger.valueOf(request.getAmount())
                                .multiply(BigInteger.TEN.pow(18))) // 假设18位小数
                ),
                Collections.<TypeReference<?>>emptyList()
        );

        return executeTransaction(request.getContractAddress(), function);
    }

    /**
     * 销毁代币
     */
    public String burn(BurnRequest request) throws Exception {
        log.info("Burning {} tokens from {}", request.getAmount(), request.getFromAddress());

        Function function;
        if (request.getFromAddress() != null && !request.getFromAddress().isEmpty()) {
            // burnFrom - 只有owner可以调用
            function = new Function(
                    "burnFrom",
                    Arrays.asList(
                            new org.web3j.abi.datatypes.Address(request.getFromAddress()),
                            new Uint256(BigInteger.valueOf(request.getAmount())
                                    .multiply(BigInteger.TEN.pow(18)))
                    ),
                    Collections.<TypeReference<?>>emptyList()
            );
        } else {
            // burn - 任何人都可以销毁自己的代币
            function = new Function(
                    "burn",
                    Arrays.asList(
                            new Uint256(BigInteger.valueOf(request.getAmount())
                                    .multiply(BigInteger.TEN.pow(18)))
                    ),
                    Collections.emptyList()
            );
        }

        return executeTransaction(request.getContractAddress(), function);
    }

    /**
     * 转账
     */
    public String transfer(TransferRequest request) throws Exception {
        log.info("Transferring {} tokens to {}", request.getAmount(), request.getToAddress());

        Function function = new Function(
                "transfer",
                Arrays.asList(
                        new org.web3j.abi.datatypes.Address(request.getToAddress()),
                        new Uint256(BigInteger.valueOf(request.getAmount())
                                .multiply(BigInteger.TEN.pow(18)))
                ),
                Collections.<TypeReference<?>>emptyList()
        );

        return executeTransaction(request.getContractAddress(), function);
    }

    /**
     * 查询余额
     */
    public BigInteger getBalance(String contractAddress, String address) throws Exception {
        Function function = new Function(
                "balanceOf",
                Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(address)),
                Arrays.<TypeReference<?>>asList(new TypeReference<Uint256>() {})
        );

        String encodedFunction = FunctionEncoder.encode(function);
        EthCall response = web3j.ethCall(
                Transaction.createEthCallTransaction(address, contractAddress, encodedFunction),
                DefaultBlockParameterName.LATEST
        ).send();

        List<Type> decoded = FunctionReturnDecoder.decode(
                response.getValue(), function.getOutputParameters()
        );

        return (BigInteger) decoded.get(0).getValue();
    }

    /**
     * 执行交易
     */
    private String executeTransaction(String contractAddress, Function function) throws Exception {
        String encodedFunction = FunctionEncoder.encode(function);

        EthGetTransactionCount ethGetTransactionCount = web3j
                .ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST)
                .send();
        BigInteger nonce = ethGetTransactionCount.getTransactionCount();

        // 创建原始交易
        RawTransaction rawTransaction = RawTransaction.createTransaction(
                nonce,
                gasProvider.getGasPrice(),
                gasProvider.getGasLimit(),
                contractAddress,
                encodedFunction
        );

        // 签名交易
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        String hexValue = Numeric.toHexString(signedMessage);

        // 发送交易
        EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();

        if (ethSendTransaction.hasError()) {
            throw new RuntimeException("Transaction failed: " + ethSendTransaction.getError().getMessage());
        }

        String transactionHash = ethSendTransaction.getTransactionHash();
        log.info("Transaction hash: {}", transactionHash);

        // 等待交易确认
        waitForTransactionReceipt(transactionHash);
        return transactionHash;
    }

    /**
     * 等待交易确认
     */
    private TransactionReceipt waitForTransactionReceipt(String transactionHash) throws Exception {
        EthGetTransactionReceipt receipt;
        do {
            Thread.sleep(1000);
            receipt = web3j.ethGetTransactionReceipt(transactionHash).send();
        } while (!receipt.getTransactionReceipt().isPresent());

        return receipt.getTransactionReceipt().get();
    }

    /**
     * 获取合约字节码
     * 优先使用已编译的字节码，如果不存在则自动编译
     */
    private String getContractBytecode() throws Exception {
        // 首先尝试读取已编译的字节码文件
        String bytecode = contractCompilerService.getCompiledBytecode();
        if (bytecode != null && !bytecode.isEmpty()) {
            log.info("Using existing compiled bytecode");
            return bytecode;
        }

        // 如果不存在，则自动编译合约
        log.info("Compiled bytecode not found, compiling contract...");
        try {
            bytecode = contractCompilerService.compileContract();
            log.info("Contract compiled successfully");
            return bytecode;
        } catch (Exception e) {
            log.error("Failed to compile contract: {}", e.getMessage(), e);
            throw new RuntimeException(
                "Failed to compile contract. Please ensure solc executable is available.\n" +
                "Either install solc and add it to system PATH, or configure 'solc.path' in application.properties\n" +
                "Error: " + e.getMessage(),
                e
            );
        }
    }
}

